#include <boost/log/trivial.hpp>
#include <folly/Conv.h>
#include <folly/logging/xlog.h>
#include <model-updater/ModelUpdater.h>

namespace flashpp {

ModelUpdater::ModelUpdater(Pipeline& pipeline, Network& network)
    : network_(network) {
  Rule rule;
  addFiberTask([&pipeline, this]() mutable noexcept {
    while (1) {
      std::shared_ptr<AtomicOverwrite> ao;
      pipeline.blockingRead(ao);
      process(*ao);
    }
  });
}

void
ModelUpdater::process(AtomicOverwrite& ao) {
  auto& device = network_.nameToDevice(ao.device);

  bool inc = true;
  if (inc) {
    for (auto& o : ao.overwrites) {
      appendPredicate(aps_, o.first);
    }
  } else {
    for (auto& o : ao.overwrites) {
      device.eportToHs()[o.second] = device.eportToHs()[o.second] | o.first;
    }
    for (auto& [devn, dev] : network_.nameToDevice()) {
      for (auto& [pn, hs] : dev.eportToHs()) {
        appendPredicate(aps_, hs);
      }
    }
  }
  XLOG(INFO) << aps_.size();
}

// void
// ModelUpdater::appendPredicate(std::set<HeaderSpace>& aps, HeaderSpace pred) {
//   if (aps.size() == 0) {
//     if (pred.isFalse() || pred.isTrue()) {
//       aps.insert(pred);
//     } else {
//       aps.insert(pred);
//       aps.insert(!pred);
//     }
//     return;
//   }

//   std::set<HeaderSpace> newAps;
//   for (auto& ap : aps) {
//     auto p = ap & pred;
//     if (!p.isFalse() && p != ap) {

//     }
    
//   }
// }

void
ModelUpdater::appendPredicate(std::set<HeaderSpace>& aps, HeaderSpace pred) {
  if (aps.size() == 0) {
    if (pred.isFalse() || pred.isTrue()) {
      aps.insert(HeaderSpace::hsAll());
    } else {
      aps.insert(pred);
      aps.insert(!pred);
    }
    return;
  }

  std::set<HeaderSpace> toRemove;
  std::set<HeaderSpace> toAdd;
  for (auto& ap : aps) {
    if (pred.isFalse()) {
      break;
    }
    auto p = ap & pred;
    if (!p.isFalse()) {
      if (p == ap) {
      // if ((p & !ap).isFalse()) {
        continue;
      } else {
        toRemove.insert(ap);
        toAdd.insert(p);

        auto np = ap - p;
        toAdd.insert(np);

        // pred = pred - p;
        pred -= p;
      }
    }
  }
  for (auto& p : toRemove) {
    aps.erase(p);
  }
  for (auto& p : toAdd) {
    aps.insert(p);
  }
}

void
ModelUpdater::findCover(
    HeaderSpace& hs,
    std::set<HeaderSpace>& apset,
    std::set<HeaderSpace>& cover) {
  for (auto& ap : apset) {
    if (!(ap & hs).isFalse()) {
      cover.insert(ap);
    }
  }
}

} // namespace flashpp